﻿#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

using Weazel.Badger.Vhdl.Types;
using Weazel.Badger.Vhdl.Expressions;
using Weazel.Badger.Vhdl.Statements;

#endregion

namespace Weazel.Badger.Vhdl
{
  public class Process : IComponent
  {
    /// <summary>
    /// The name of this process
    /// </summary>
    private string name;

    private List<IProcessDeclarativeItem> declarativeItems =
      new List<IProcessDeclarativeItem>();
    
    /// <summary>
    /// List of signals that this process is sensitive to
    /// </summary>
    private List<Signal> sensitivityListSignals = new List<Signal>();
    public List<Signal> SensitivityListSignals
    {
      get
      {
        return sensitivityListSignals;
      }
    }

    /// <summary>
    /// List of ports that this process is sensitive to
    /// </summary>
    private List<Port> sensitivityListPorts = new List<Port>();
    public List<Port> SensitivityListPorts
    {
      get
      {
        return sensitivityListPorts;
      }
    }

    /// <summary>
    /// Make this process sensitive to a signal or port.
    /// </summary>
    /// <param name="signal">Signal to make this process sensitive to</param>
    public void AddSignalToSensivitityList(Signal signal)
    {      
      if (signal is Port)
      {
        if(!sensitivityListPorts.Contains((Port)signal))
          sensitivityListPorts.Add((Port)signal);
      }
      else
      {
        if(!SensitivityListSignals.Contains(signal))
          sensitivityListSignals.Add(signal);
      }
    }

    public bool ConstainsConstant(ConstantDeclaration constant)
    {
      foreach (IProcessDeclarativeItem i in declarativeItems)
      {
        if (i is ConstantDeclaration)
        {
          ConstantDeclaration c = (ConstantDeclaration)i;
          if (c.Name == constant.Name)
            return true;
        }
      }

      return false;
    }

    /// <summary>
    /// Add a constant to this process. This method will check that the constant
    /// does not already exist.
    /// </summary>
    /// <param name="constant">Constant to add</param>
    public void AddConstant(ConstantDeclaration constant)
    {
      foreach (IProcessDeclarativeItem i in declarativeItems)
      {
        if (i is ConstantDeclaration)
        {
          ConstantDeclaration c = (ConstantDeclaration)i;

          if (c.Name == constant.Name)
          {
            if (c.Type != constant.Type || c.Value != constant.Value)
            {
              string message =
                string.Format(
                  "Constant with the name '{0}' already defined for process '{0}'",
                  constant.Name,
                  name
                );

              throw new VhdlModelException(message);
            }
            else
            {
              // already here ..
              return;
            }
          }
        }
      }

      this.declarativeItems.Add(constant);
    }

    public void AddDeclarativeItem(IProcessDeclarativeItem item)
    {
      if (item is ConstantDeclaration)
      {
        AddConstant((ConstantDeclaration)item);
      }
      else
      {
        this.declarativeItems.Add(item);
      }
    }

    /// <summary>
    /// The statements of this process
    /// </summary>
    private StatementCollection statements;
    public StatementCollection Statements
    {
      get
      {
        return statements;
      }

      set
      {
        statements = value;
      }
    }

    public void Verify(ref List<VerifyMessage> messages)
    {      
      statements.Verify(ref messages);
    }

		public void Resolve(Entity entity)
    {
			statements.Resolve(entity, this, Statements);
    }
    
    public Process(string name, Entity entity)
    {
      this.name = name;
      this.statements = new StatementCollection();
    }

    private void writeSensitivityList(System.CodeDom.Compiler.IndentedTextWriter writer)
    {
      writer.Indent++;

      writer.WriteLine("-- sensitive to signals:");

      if (this.sensitivityListSignals.Count > 0)
      {
        for (int i = 0; i < sensitivityListSignals.Count; i++)
        {
          if (i != sensitivityListSignals.Count - 1 || sensitivityListPorts.Count > 0)
            writer.WriteLine(sensitivityListSignals[i].Name + ",");
          else
            writer.WriteLine(sensitivityListSignals[i].Name);
        }
      }
      else
      {
        writer.WriteLine("-- none");
      }

      writer.WriteLine("-- sensitive to ports: ");
      if (sensitivityListPorts.Count > 0)
      {
        for (int i = 0; i < sensitivityListPorts.Count; i++)
        {
          if (i != sensitivityListPorts.Count - 1)
            writer.WriteLine(sensitivityListPorts[i].Name + ",");
          else
            writer.WriteLine(sensitivityListPorts[i].Name);
        }
      }
      else
      {
        writer.WriteLine("-- none");
      }

      writer.Indent--;

      writer.WriteLine(")");
    }

    private void writeDeclarations(System.CodeDom.Compiler.IndentedTextWriter writer)
    {
      writer.WriteLine("is");
      writer.Indent++;

      foreach (IProcessDeclarativeItem item in declarativeItems)
        item.Write(writer);

      writer.Indent--;
    }

    public void Write(System.CodeDom.Compiler.IndentedTextWriter writer)
    {
			if (statements.Count == 0)
			{
				// do not write empty processes 
				writer.Write("-- process '{0}' not written (no statements)", name);
				return;

			}

      writer.Write(String.Format("{0} : process ", name));

      if (sensitivityListPorts.Count != 0 || SensitivityListSignals.Count != 0)
      {
        writer.WriteLine("(");
        writeSensitivityList(writer);
      }
      else
        writer.WriteLine();

      if (declarativeItems.Count > 0)
        writeDeclarations(writer);

      writer.WriteLine("begin");
      writer.Indent++;

      statements.Write(writer);

      writer.Indent--;
      writer.WriteLine("end process {0};", name);
    }
  }
}
